package dtm;

import org.junit.jupiter.api.Test;
import tm.EnumDirection;
import tm.TMConfiguration;
import tm.TMRule;
import tm.Tape;

import java.util.Arrays;
import java.util.List;
import java.util.Set;

import static org.junit.jupiter.api.Assertions.*;

/**
 * @author haoly
 * @title DTMTest
 * @date 2025/4/23 14:53
 * @description 确定性图灵机测试类
 */
public class DTMTest {
    @Test
    public void testRun(){
        List<TMRule<Integer>> rules = Arrays.asList(
                new TMRule<>(1, '0', 2, '1', EnumDirection.RIGHT),
                new TMRule<>(1, '1', 1, '0', EnumDirection.LEFT),
                new TMRule<>(1, '_', 2, '1', EnumDirection.RIGHT),
                new TMRule<>(2, '0', 2, '0', EnumDirection.RIGHT),
                new TMRule<>(2, '1', 2, '1', EnumDirection.RIGHT),
                new TMRule<>(2, '_', 3, '_', EnumDirection.LEFT)
        );

        DTMRulebook<Integer> rulebook = new DTMRulebook<>(rules);

        char blankChar = '_';
        Tape tape = new Tape(List.of('1','0','1'), '1', List.of(), blankChar);

        TMConfiguration<Integer> configuration = new TMConfiguration<>(1, tape);

        DTM<Integer> dtm = new DTM<>(configuration, Set.of(3), rulebook);
        System.out.println(dtm.getCurrentConfiguration());

        assertFalse(dtm.accepting());

        dtm.step();
        System.out.println(dtm.getCurrentConfiguration());
        assertFalse(dtm.accepting());

        dtm.run();

        System.out.println(dtm.getCurrentConfiguration());
        assertTrue(dtm.accepting());
    }

    @Test
    public void testStuck(){
        List<TMRule<Integer>> rules = Arrays.asList(
                new TMRule<>(1, '0', 2, '1', EnumDirection.RIGHT),
                new TMRule<>(1, '1', 1, '0', EnumDirection.LEFT),
                new TMRule<>(1, '_', 2, '1', EnumDirection.RIGHT),
                new TMRule<>(2, '0', 2, '0', EnumDirection.RIGHT),
                new TMRule<>(2, '1', 2, '1', EnumDirection.RIGHT),
                new TMRule<>(2, '_', 3, '_', EnumDirection.LEFT)
        );

        DTMRulebook<Integer> rulebook = new DTMRulebook<>(rules);

        char blankChar = '_';
        Tape tape = new Tape(List.of('1','2','1'), '1', List.of(), blankChar);
        TMConfiguration<Integer> configuration = new TMConfiguration<>(1, tape);

        DTM<Integer> dtm = new DTM<>(configuration, Set.of(3), rulebook);
        dtm.run();

        System.out.println(dtm.getCurrentConfiguration());
        assertFalse(dtm.accepting());
        assertTrue(dtm.stuck());
    }

    @Test
    public void testAFollowedByEqualNumOfBandC(){
        List<TMRule<Integer>> rules = Arrays.asList(
                // state 1: X/X;R
                new TMRule<>(1, 'X', 1, 'X', EnumDirection.RIGHT),
                // state 1: a/X;R
                new TMRule<>(1, 'a', 2, 'X', EnumDirection.RIGHT),
                // state 1: _/_;L
                new TMRule<>(1, '_', 6, '_', EnumDirection.LEFT),
                // state 2: a/a;R
                new TMRule<>(2, 'a', 2, 'a', EnumDirection.RIGHT),
                // state 2: X/X;R
                new TMRule<>(2, 'X', 2, 'X', EnumDirection.RIGHT),
                // state 2: b/X;R
                new TMRule<>(2, 'b', 3, 'X', EnumDirection.RIGHT),
                // state 3: b/b;R
                new TMRule<>(3, 'b', 3, 'b', EnumDirection.RIGHT),
                // state 3: X/X;R
                new TMRule<>(3, 'X', 3, 'X', EnumDirection.RIGHT),
                // state 3: c/X;R
                new TMRule<>(3, 'c', 4, 'X', EnumDirection.RIGHT),
                // state 4: c/c;R
                new TMRule<>(4, 'c', 4, 'c', EnumDirection.RIGHT),
                // state 4: _/_;L
                new TMRule<>(4, '_', 5, '_', EnumDirection.LEFT),
                // state 5: a/a;L
                new TMRule<>(5, 'a', 5, 'a', EnumDirection.LEFT),
                // state 5: b/b;L
                new TMRule<>(5, 'b', 5, 'b', EnumDirection.LEFT),
                // state 5: c/c;L
                new TMRule<>(5, 'c', 5, 'c', EnumDirection.LEFT),
                // state 5: X/X;L
                new TMRule<>(5, 'X', 5, 'X', EnumDirection.LEFT),
                // state 5: _/_;R
                new TMRule<>(5, '_', 1, '_', EnumDirection.RIGHT)
        );

        DTMRulebook<Integer> rulebook = new DTMRulebook<>(rules);

        char blankChar = '_';
        Tape tape = new Tape(List.of(), 'a', List.of('a','a','b','b','b','c', 'c', 'c'), blankChar);

        TMConfiguration<Integer> configuration = new TMConfiguration<>(1, tape);

        System.out.println(configuration);

        DTM<Integer> dtm = new DTM<>(configuration, Set.of(6), rulebook);
        for(int i = 0; i < 10; i++){
            dtm.step();
        }
        System.out.println(dtm.getCurrentConfiguration());

        for(int i = 0; i < 25; i++){
            dtm.step();
        }
        System.out.println(dtm.getCurrentConfiguration());

        dtm.run();

        System.out.println(dtm.getCurrentConfiguration());
        assertTrue(dtm.accepting());
        assertEquals(6, dtm.getCurrentConfiguration().getState());
    }
}
